#include <assert.h>
#include <irrlicht.h>
#include "demo.h"
#include "fx_hallintaa.h"


using namespace irr;
using namespace irr::core;
using namespace irr::scene;


static ISceneManager *sSceneManager = 0;
static ISceneNode *sTaatHallitsee = 0;
static ISceneNode *sKuori1 = 0;
static ISceneNode *sKuori2 = 0;
static ISceneNode *sKuori3 = 0;
static ISceneNode *sKuori4 = 0;

struct PosRot
{
    vector3df pos;
    vector3df rot; // euler angles used by irr
};

static array<PosRot> sPath;


bool HallintaaFx::init()
{
    if (!gIrrDevice || !gIrrDevice->getSceneManager())
        return false;

    //bool zipRes = gIrrDevice->getFileSystem()->addFileArchive("data_oldies_hallintaa.zip", false, false, io::EFAT_ZIP);
    //assert(zipRes);

    sSceneManager = gIrrDevice->getSceneManager()->createNewSceneManager();

    ISceneManager *sceneManager = sSceneManager;

    sceneManager->loadScene("data/oldies/hallintaa.irr");

    //ISceneNode *light = sceneManager->getSceneNodeFromType(scene::ESNT_LIGHT);

    sTaatHallitsee = sceneManager->getSceneNodeFromName("kuori0");
	sKuori1 = sceneManager->getSceneNodeFromName("kuori1");
	sKuori2 = sceneManager->getSceneNodeFromName("kuori2");
	sKuori3 = sceneManager->getSceneNodeFromName("kuori3");
	sKuori4 = sceneManager->getSceneNodeFromName("kuori4");

    sceneManager->addCameraSceneNode();
    vector3df cameraPosition(2, 2, 2);
    sceneManager->getActiveCamera()->setPosition(cameraPosition);
	sceneManager->getActiveCamera()->setTarget(sTaatHallitsee->getPosition());

	// pick path nodes
    {
        int nextNodeId = 0;
        bool more;
        do
        {
            more = false;
            ISceneNode *sn = sceneManager->getSceneNodeFromId(nextNodeId++);
            if (sn)
            {
                PosRot pr;
                pr.pos = sn->getPosition();
                pr.rot = sn->getRotation();
                sPath.push_back(pr);
                more = true;				
            }
        } while (more);
    }
	

    mInitialized = true;
    return true;
}

static s32 sClampSplineIndex(s32 idx, s32 size)
{
	//return ( idx<0 ? size+idx : ( idx>=size ? idx-size : idx ) );  // loop
    if (idx < 0)
        idx = 0;
    if (idx >= size)
        idx = size - 1;
    //if (idx >= size)
    //    idx = size - 3 + idx % 3; // loop last 3 control points
    return idx;
}

// normalizedPosition should be time in range [0..1]
static core::vector3df sInterpolatePosition(const array<PosRot> &keys, float normalizedPosition, float tightness = 0.5f)
{
    core::vector3df result(0, 0, 0);

    const int pSize = (int)keys.size();
    if (pSize == 0)
        return result;

    // Spline-interpolate position
    // (Spline interpolation code is taken from Irrlicht's CSceneNodeAnimatorFollowSpline)

    const f32 dt = normalizedPosition * pSize;
    const f32 u = core::fract(dt);
    const s32 idx = core::floor32(dt); // % pSize; - disabled wrap-around...

    s32 idx0 = sClampSplineIndex(idx - 1, pSize);
    s32 idx1 = sClampSplineIndex(idx, pSize);
    s32 idx2 = sClampSplineIndex(idx + 1, pSize);
    s32 idx3 = sClampSplineIndex(idx + 2, pSize);
    const core::vector3df p0 = keys[idx0].pos;
    const core::vector3df p1 = keys[idx1].pos;
    const core::vector3df p2 = keys[idx2].pos;
    const core::vector3df p3 = keys[idx3].pos;

    // hermite polynomials
    const f32 h1 = 2.0f * u * u * u - 3.0f * u * u + 1.0f;
    const f32 h2 = -2.0f * u * u * u + 3.0f * u * u;
    const f32 h3 = u * u * u - 2.0f * u * u + u;
    const f32 h4 = u * u * u - u * u;

    // tangents
    const core::vector3df t1 = ( p2 - p0 ) * tightness;
    const core::vector3df t2 = ( p3 - p1 ) * tightness;

    // interpolated point
    result = p1 * h1 + p2 * h2 + t1 * h3 + t2 * h4;
    
    return result;
}


void HallintaaFx::render(double time, int timeMs)
{
    vector3df eulerAngles((float)gTimeMs / 40.0f, (float)gTimeMs / 20.0f, (float)gTimeMs / 30.0f);	
	sKuori2->setRotation(eulerAngles);
	sKuori3->setRotation(-eulerAngles);
	sKuori4->setRotation(-eulerAngles);
	vector3df eulerAnglesTaat(0.0f, (float)gTimeMs / 40.0f, 0.0f);
	sTaatHallitsee->setRotation(eulerAnglesTaat);
	vector3df eulerAnglesK0(0.0f, (float)gTimeMs / 20.0f, 0.0f);
	sKuori1->setRotation(eulerAnglesK0);

	float t = (float)(time - mFxStartTime);
	float camTargetTime = (float)(t / (mFxEndTime - mFxStartTime));
	if (camTargetTime > 0.76f)
		camTargetTime = 0.76f;
    vector3df pos = sInterpolatePosition(sPath, camTargetTime);

    // camera comes 1sec behind, a bit higher
	float camTime = (float)((t - 1.0f) / (mFxEndTime - mFxStartTime));
	if (camTime > 0.75f)
		camTime = 0.75f;
    vector3df posc = sInterpolatePosition(sPath, camTime);
    sSceneManager->getActiveCamera()->setPosition(posc);
	sSceneManager->getActiveCamera()->setTarget(sKuori1->getPosition()); // target next camera node

    /*const float endTransitionLength = (float)(mRenderEndTime - mFxEndTime);
    float fxEndTransitionScale = 1.0f - sBoxStep((float)time, (float)mFxEndTime, (float)mFxEndTime + endTransitionLength);
	sKuori4->setScale(vector3df(fxEndTransitionScale, fxEndTransitionScale, fxEndTransitionScale));*/
	sSceneManager->drawAll();
}


void HallintaaFx::deinit()
{
    sSceneManager->drop();
    mInitialized = false;
}
